using DevExpress.Blazor;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Blazor.Services;
using EasyXaf.LowCode.RulesEngineEditors.Models;
using EasyXaf.LowCode.RulesEngineEditors.UndoManagers;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Newtonsoft.Json;
using RulesEngine.Models;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;

namespace EasyXaf.LowCode.RulesEngineEditors;

public sealed partial class RuleNode : IDisposable
{
    private static readonly Dictionary<string, string>[] Colors =
    [
        new() { ["BorderColor"] = "#2980B9", ["BackColor"] = "#2980B933" },
        new() { ["BorderColor"] = "#8E44AD", ["BackColor"] = "#8E44AD33" },
        new() { ["BorderColor"] = "#D35400", ["BackColor"] = "#D3540033" },
    ];

    private DxContextMenu StartNodeContextMenu { get; set; }

    private DxContextMenu RuleContextMenu { get; set; }

    private DxContextMenu OperatorContextMenu { get; set; }

    private DxContextMenu NestedRuleOutputModeContextMenu { get; set; }

    private DxContextMenu AddParentRuleContextMenu { get; set; }

    private string NodeBackColor => Colors[Rule.Level % 3]["BackColor"];

    private string NodeBorderColor => Colors[Rule.Level % 3]["BorderColor"];

    private string NodeContentStyle
    {
        get
        {
            if (IsStartNode)
            {
                return "background-color:#27AE6033;border-color:#27AE60;";
            }
            else
            {
                return $"background-color:{NodeBackColor};border-color:{NodeBorderColor};";
            }
        }
    }

    private string NodeClassName
    {
        get
        {
            var className = "rule-node ";

            if (!Rule.Enabled)
            {
                className += "disabled-rule ";
            }

            if (Rule.IsFirstRule)
            {
                className += "first-rule ";
            }

            if (Rule.IsLastRule)
            {
                className += "last-rule ";
            }

            if (Rule.Rules.Any())
            {
                className += "has-child-rules ";
            }

            if (Rule.IsCollapse)
            {
                className += "collapse-rule ";
            }

            return className.Trim();
        }
    }

    private string ValidationResultIconColor
    {
        get
        {
            var color = "#27AE60";

            if (ValidationResults.Any(r => r.Level == ValidationResultLevel.Error))
            {
                color = "#C0392B";
            }
            else if (ValidationResults.Any(r => r.Level == ValidationResultLevel.Warn))
            {
                color = "#F39C12";
            }

            return color;
        }
    }

    private int RuleIndex => BrotherRules.IndexOf(Rule);

    private bool IsStartNode => Rule is StartObject;

    private bool CanMoveUp => !IsReadOnly && (!Rule.IsFirstRule || (Rule.Parent == null && BrotherRules.IndexOf(Rule) != 1));

    private bool CanMoveDown => !IsReadOnly && !Rule.IsLastRule;

    private bool CanMoveTop => !IsReadOnly && (!Rule.IsFirstRule || (Rule.Parent == null && BrotherRules.IndexOf(Rule) != 1));

    private bool CanMoveBottom => !IsReadOnly && !Rule.IsLastRule;

    private bool CanPaste => !IsReadOnly && Workflow.LastClonedRule != null;

    private string ValidationResultInfo
    {
        get
        {
            var resultInfo = new StringBuilder();

            foreach (var rule in ValidationResults)
            {
                resultInfo.AppendLine(rule.Message);
            }

            return resultInfo.ToString();
        }
    }

    private IEnumerable<ValidationResult> ValidationResults => IsStartNode ? Workflow.ValidationResults : Rule.ValidationResults;

    private WorkflowObject Workflow => Rule.Workflow;

    private IUndoManager UndoManager => Workflow.UndoManager;

    private ObservableCollection<RuleObject> BrotherRules => Rule.Parent?.Rules ?? Workflow.Rules;

    private ObservableCollection<RuleObject> ChildRules => Rule.Rules;

    [Parameter]
    public bool IsReadOnly { get; set; }

    [Parameter]
    public RuleObject Rule { get; set; }

    [Parameter]
    public EventCallback RuleChanged { get; set; }

    [Inject]
    private IXafApplicationProvider ApplicationProvider { get; set; }

    private void AddRule(bool before)
    {
        UndoManager.AddRule(new RuleObject(), Rule.Parent, RuleIndex + (before ? 0 : 1), Workflow);
    }

    private void AddChildRule()
    {
        UndoManager.AddChildRule(Rule, new RuleObject());
    }

    private void AddParentRule()
    {
        UndoManager.AddParentRule(Rule, new RuleObject());
    }

    private void AddParentForRules()
    {
        UndoManager.AddParentForRules(Rule, new RuleObject());
    }

    private void ExpandOrCollapseChildRules()
    {
        UndoManager.ExpandOrCollapseChildRules(Rule);
    }

    private void MoveRule(int index)
    {
        UndoManager.ChangeRuleOrder(Rule, (Rule.Parent == null && index <= 0) ? 1 : index);
    }

    private void CopyRule()
    {
        Workflow.LastClonedRule = Rule.Clone();
    }

    private void CutRule()
    {
        Workflow.LastClonedRule = Rule.Clone();
        DeleteRule();
    }

    private void PasteRule()
    {
        if (Workflow.LastClonedRule != null)
        {
            var rule = Workflow.LastClonedRule.Clone();
            var parent = Rule.Parent != null ? rule : null;
            var index = Rule.Parent != null ? Rule.Rules.Count : Workflow.Rules.Count;
            UndoManager.AddRule(rule, parent, index, Workflow);
        }
    }

    private void DeleteRule()
    {
        UndoManager.DeleteRule(Rule);
    }

    private void DeleteAndPreserveChildRules()
    {
        UndoManager.DeleteAndPreserveChildRules(Rule);
    }

    private void ValidateRule()
    {
        Rule.Validate();
    }

    private void ValidateWorkflow()
    {
        Workflow.Validate();
    }

    private void ChangeRuleOperator(RuleOperator ruleOperator)
    {
        if (Rule.Operator != ruleOperator)
        {
            UndoManager.ChangeRuleOperator(Rule, ruleOperator);
        }
    }

    private void ChangeNestedRuleOutputMode(NestedRuleOutputMode nestedRuleOutputMode)
    {
        if (Rule.NestedRuleOutputMode != nestedRuleOutputMode)
        {
            UndoManager.ChangeNestedRuleOutputMode(Rule, nestedRuleOutputMode);
        }
    }

    private void ShowValidationResults()
    {
        var application = ApplicationProvider.GetApplication();
        var objectSpace = application.CreateObjectSpace(typeof(ValidationResult));

        var collectionSource = new CollectionSource(objectSpace, typeof(ValidationResult));
        foreach (var validationResult in ValidationResults)
        {
            collectionSource.Add(validationResult);
        }

        var viewId = application.GetListViewId(typeof(ValidationResult));
        var view = application.CreateListView(viewId, collectionSource, true);

        application.ShowViewStrategy.ShowViewInPopupWindow(view);
    }

    private void PopupRuleJsonWindow()
    {
        var rule = Rule.ToRule();
        var rules = rule.Rules;
        rule.Rules = null;

        var json = JsonConvert.SerializeObject(rule, Formatting.Indented);
        var jsonObject = new JsonObject { Json = json };

        var application = ApplicationProvider.GetApplication();
        var objectSpace = application.CreateObjectSpace(typeof(JsonObject));
        var viewId = application.GetDetailViewId(typeof(JsonObject));
        var view = application.CreateDetailView(objectSpace, viewId, true, objectSpace.GetObject(jsonObject));

        void ObjectSpace_Committed(object sender, EventArgs e)
        {
            if (json != jsonObject.Json)
            {
                rule = JsonConvert.DeserializeObject<Rule>(jsonObject.Json);
                rule.Rules = rules;
                var ruleObject = RuleObject.FromRule(rule, Rule.Parent, Workflow);
                UndoManager.UpdateRule(Rule, ruleObject);
            }
        }

        application.ShowViewStrategy.ShowViewInPopupWindow(view, () =>
        {
            objectSpace.SetModified(jsonObject);
            objectSpace.Committed -= ObjectSpace_Committed;
            objectSpace.Committed += ObjectSpace_Committed;
        });
    }

    private void PopupWorkflowJsonWindow()
    {
        var json = JsonConvert.SerializeObject(Workflow.ToWorkflow(), Formatting.Indented);
        var jsonObject = new JsonObject { Json = json };

        var application = ApplicationProvider.GetApplication();
        var objectSpace = application.CreateObjectSpace(typeof(JsonObject));
        var viewId = application.GetDetailViewId(typeof(JsonObject));
        var view = application.CreateDetailView(objectSpace, viewId, true, objectSpace.GetObject(jsonObject));

        void ObjectSpace_Committed(object sender, EventArgs e)
        {
            if (json != jsonObject.Json)
            {
                var rulesEngineWorkflow = JsonConvert.DeserializeObject<RulesEngineWorkflow>(jsonObject.Json);
                UndoManager.UpdateWorkflow(Workflow, rulesEngineWorkflow);
            }
        }

        application.ShowViewStrategy.ShowViewInPopupWindow(view, () =>
        {
            objectSpace.SetModified(jsonObject);
            objectSpace.Committed -= ObjectSpace_Committed;
            objectSpace.Committed += ObjectSpace_Committed;
        });
    }

    private void PopupRulePropertyWindow()
    {
        var rule = RuleObject.FromRule(Rule.ToRule(), Rule.Parent, Workflow);
        var json = rule.ToJson();

        var application = ApplicationProvider.GetApplication();
        var objectSpace = application.CreateObjectSpace(typeof(RuleObject));
        var viewId = application.GetDetailViewId(typeof(RuleObject));
        var view = application.CreateDetailView(objectSpace, viewId, true, objectSpace.GetObject(rule));

        void ObjectSpace_Committed(object sender, EventArgs e)
        {
            if (json != rule.ToJson())
            {
                UndoManager.UpdateRule(Rule, rule);
            }
        }

        application.ShowViewStrategy.ShowViewInPopupWindow(view, () =>
        {
            objectSpace.SetModified(rule);
            objectSpace.Committed -= ObjectSpace_Committed;
            objectSpace.Committed += ObjectSpace_Committed;
        });
    }

    private void PopupWorkflowPropertyWindow()
    {
        var workflow = Workflow.Clone();
        var json = JsonConvert.SerializeObject(workflow.ToWorkflow());

        var application = ApplicationProvider.GetApplication();
        var objectSpace = application.CreateObjectSpace(typeof(WorkflowObject));
        var viewId = application.GetDetailViewId(typeof(WorkflowObject));
        var view = application.CreateDetailView(objectSpace, viewId, true, objectSpace.GetObject(workflow));

        void ObjectSpace_Committed(object sender, EventArgs e)
        {
            if (json != JsonConvert.SerializeObject(workflow.ToWorkflow()))
            {
                UndoManager.UpdateWorkflow(Workflow, workflow);
            }
        }

        application.ShowViewStrategy.ShowViewInPopupWindow(view, () =>
        {
            objectSpace.SetModified(workflow);
            objectSpace.Committed -= ObjectSpace_Committed;
            objectSpace.Committed += ObjectSpace_Committed;
        });
    }

    private void PopupPropertyWindow()
    {
        if (IsStartNode)
        {
            PopupWorkflowPropertyWindow();
        }
        else
        {
            PopupRulePropertyWindow();
        }
    }

    private async Task ShowRuleContextMenuAsync(MouseEventArgs args)
    {
        if (Rule is StartObject)
        {
            await StartNodeContextMenu.ShowAsync(args);
        }
        else
        {
            await RuleContextMenu.ShowAsync(args);
        }
    }

    private async Task ShowOperatorContextMenuAsync(MouseEventArgs args)
    {
        if (!IsReadOnly)
        {
            await OperatorContextMenu.ShowAsync(args);
        }
    }

    private async Task ShowNestedRuleOutputModeContextMenuAsync(MouseEventArgs args)
    {
        if (!IsReadOnly)
        {
            await NestedRuleOutputModeContextMenu.ShowAsync(args);
        }
    }

    private async Task ShowAddParentRuleContextMenuAsync(MouseEventArgs args)
    {
        if (!IsReadOnly)
        {
            await AddParentRuleContextMenu.ShowAsync(args);
        }
    }

    private void Rule_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        RuleChanged.InvokeAsync();
    }

    private void Rule_ValidationResultsChanged(object sender, EventArgs e)
    {
        InvokeAsync(StateHasChanged);
    }

    protected override void OnInitialized()
    {
        base.OnInitialized();

        Rule.PropertyChanged += Rule_PropertyChanged;
        Rule.ValidationResultsChanged += Rule_ValidationResultsChanged;
    }

    public void Dispose()
    {
        Rule.PropertyChanged -= Rule_PropertyChanged;
        Rule.ValidationResultsChanged -= Rule_ValidationResultsChanged;
    }
}